﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using FundamentalsOfCompiling;

namespace CourseDesign_01_LexicalAnalysis
{
    class WordParser
    {
        //reservedWords用来保存保留字和其相应的词法类型
        ReservedTable[] reservedWords = { 
	    new ReservedTable("program",LexType.PROGRAM),
	    new ReservedTable("type",LexType.TYPE),
	    new ReservedTable("var",LexType.VAR),
	    new ReservedTable("procedure",LexType.PROCEDURE),
	    new ReservedTable("begin",LexType.BEGIN),
	    new ReservedTable("end",LexType.END),
	    new ReservedTable("array",LexType.ARRAY),
	    new ReservedTable("of",LexType.OF),
	    new ReservedTable("record",LexType.RECORD),
	    new ReservedTable("if",LexType.IF),
	    new ReservedTable("then",LexType.THEN),
	    new ReservedTable("else",LexType.ELSE),
	    new ReservedTable("fi",LexType.FI),
	    new ReservedTable("while",LexType.WHILE),
	    new ReservedTable("do",LexType.DO),
	    new ReservedTable("endwh",LexType.ENDWH),
	    new ReservedTable("read",LexType.READ),
	    new ReservedTable("write",LexType.WRITE),
	    new ReservedTable("return",LexType.RETURN),
	    new ReservedTable("integer",LexType.INTEGER),
	    new ReservedTable("char",LexType.CHAR1)
	    };
        private StreamReader source = null;

        // private StreamReader sout = null;
        private string tokenString = null;
        private string lineBuf = null;//一行字符串
        private int length;//buffer的长度
        private int linepos = 0;//字符串的指针，标识处理位置
        private bool EOF_flag;//表示文件结束
        private Global global;

        private List<TokenType> list = new List<TokenType>();//存储token序列的链表

        MainWindow mainWindow;

        public WordParser(Global g, MainWindow main)
        {
            this.global = g;
            linepos = 0;
            EOF_flag = false;
            global.operateError = false;
            global.operateTokentnum = 0;
            global.operatelineno = 0;
            length = 0;
            //  sin = new StreamReader(path);
            mainWindow = main;
        }


        public void getTokenList(String path)
        {
            global.operatelineno = 0;
            linepos = 0;
            length = 0;
            global.operateTokentnum = 0;
            EOF_flag = false;
            global.operateError = false;
            source = new StreamReader(path);
            if (source == null)
            {
                mainWindow.outPutLine("未找到源文件");
                global.operateError = true;
                return;
            }

            TokenType currentToken = new TokenType();
            do
            {
                StateType state = StateType.START;
                bool save;
                tokenString = "";
                while (state != StateType.DONE)
                {
                    int c = getNextChar();
                    save = true;
                    switch (state)
                    {
                        case StateType.START:
                            if (isdigit(c))
                                state = StateType.INNUM;

                               /* 当前字符c为字母,当前DFA状态state设置为标识符状态INID *
                                * 确定性有限自动机DFA处于标识符类型单词中              */
                            else if (isalpha(c))
                                state = StateType.INID;
                            else if (c == ':')
                                state = StateType.INASSIGN;

                          /* 当前字符c为.,当前DFA状态state设置为数组下标界限状态*/
                            /* INRANGE，确定性有限自动机DFA处于数组下标界限类型单词中*/
                            else if (c == '.')
                                state = StateType.INRANGE;

                            else if (c == '\'')
                            {
                                save = false;
                                state = StateType.INCHAR;
                            }
                            /* 当前字符c为空白(空格,制表符,换行符),字符存储标志save设置为FALSE *
                           * 当前字符为分隔符,不需要产生单词,无须存储 */
                            else if ((c == ' ') || (c == '\t') || (c == '\n'))
                                save = false;

                            /* 当前字符c为左括号,字符存储标志save设置为FALSE     *
                             * 当前DFA状态state设置为注释状态INCOMMENT			  *	
                             * 确定性有限自动机DFA处于注释中,不生成单词,无需存储 */
                            else if (c == '{')
                            {
                                save = false;
                                state = StateType.INCOMMENT;
                            }
                            else
                            {
                                state = StateType.DONE;
                                switch (c)
                                {
                                    case -1://-1表示文件结束
                                        save = false;
                                        currentToken.Lex = LexType.ENDFILE1;//表示文件结束
                                        break;

                                    /* 当前字符c为"=",当前识别单词返回值currentToken设置为等号单词EQ */
                                    case '=':
                                        currentToken.Lex = LexType.EQ;
                                        break;

                                    /* 当前字符c为"<",当前识别单词返回值currentToken设置为小于单词LT */
                                    case '<':
                                        currentToken.Lex = LexType.LT;
                                        break;

                                    /* 当前字符c为"+",当前识别单词返回值currentToken设置为加号单词PLUS */
                                    case '+':
                                        currentToken.Lex = LexType.PLUS;
                                        break;

                                    /* 当前字符c为"-",当前识别单词返回值currentToken设置为减号单词MINUS */
                                    case '-':
                                        currentToken.Lex = LexType.MINUS;
                                        break;

                                    /* 当前字符c为"*",当前识别单词返回值currentToken设置为乘号单词TIMES */
                                    case '*':
                                        currentToken.Lex = LexType.TIMES;
                                        break;

                                    /* 当前字符c为"/",当前识别单词返回值currentToken设置为除号单词OVER */
                                    case '/':
                                        currentToken.Lex = LexType.OVER;
                                        break;
                                    case '[':
                                        currentToken.Lex = LexType.LMIDPAREN;
                                        break;
                                    case ']':
                                        currentToken.Lex = LexType.RMIDPAREN;
                                        break;
                                    case '(':
                                        currentToken.Lex = LexType.LPAREN;
                                        break;

                                    /* 当前字符c为")",当前识别单词返回值currentToken设置为右括号单词RPAREN */
                                    case ')':
                                        currentToken.Lex = LexType.RPAREN;
                                        break;

                                    /* 当前字符c为";",当前识别单词返回值currentToken设置为分号单词SEMI */
                                    case ';':
                                        currentToken.Lex = LexType.SEMI;
                                        break;
                                    /* 当前字符c为",",当前识别单词返回值currentToken设置为逗号单词COMMA */
                                    case ',':
                                        currentToken.Lex = LexType.COMMA;
                                        break;


                                    /* 当前字符c为其它字符,当前识别单词返回值currentToken设置为错误单词ERROR1 */
                                    default:
                                        currentToken.Lex = LexType.ERROR1;
                                        global.operateError = true;
                                        break;
                                }
                            }//里层的switch
                            break;
                        case StateType.INCOMMENT:
                            save = false;
                            if (c == -1)
                            {
                                state = StateType.DONE;
                                currentToken.Lex = LexType.ENDFILE1;

                            }
                            else if (c == '}') state = StateType.START;
                            break;
                        /* 当前DFA状态state为赋值状态INASSIGN,确定性有限自动机DFA处于赋值单词位置 */
                        case StateType.INASSIGN:
                            state = StateType.DONE;
                            if (c == '=')
                                currentToken.Lex = LexType.ASSIGN;
                            /* 当前字符c为其它字符,即":"后不是"=",在输入行缓冲区中回退一个字符       *
                             * 字符存储状态save设置为FALSE,当前识别单词返回值currentToken设置为ERROR1 */
                            else
                            {
                                ungetNextChar();
                                save = false;
                                currentToken.Lex = LexType.COLON;
                            }
                            break;

                        case StateType.INRANGE:
                            /* 当前DFA状态state设置为完成状态DONE,赋值单词结束 */
                            state = StateType.DONE;
                            /* 当前字符c为".",当前识别单词返回值currentToken设置为下标界UNDERANGE */
                            if (c == '.')
                                currentToken.Lex = LexType.UNDERANGE;
                            /* 当前字符c为其它字符,即"."后不是".",在输入行缓冲区中回退一个字符       *
                             * 字符存储状态save设置为FALSE,当前识别单词返回值currentToken设置为ERROR1 */
                            else
                            {
                                ungetNextChar();
                                save = false;
                                currentToken.Lex = LexType.DOT;
                            }
                            break;

                        /* 当前DFA状态state为数字状态INNUM,确定性有限自动机处于数字单词位置 */
                        case StateType.INNUM:

                            /* 当前字符c不是数字,则在输入行缓冲区源中回退一个字符					*
                             * 字符存储标志设置为FALSE,当前DFA状态state设置为DONE,数字单词识别完成 *
                             * 当前识别单词返回值currentToken设置为数字单词NUM                     */
                            if (!isdigit(c))
                            {
                                ungetNextChar();
                                save = false;
                                state = StateType.DONE;
                                currentToken.Lex = LexType.INTC;
                            }
                            break;
                        case StateType.INCHAR:

                            if (isalnum(c))
                            {
                                int c1 = getNextChar();
                                if (c1 == '\'')
                                {
                                    save = true;
                                    state = StateType.DONE;
                                    currentToken.Lex = LexType.CHARC;
                                }
                                else
                                {
                                    ungetNextChar();
                                    ungetNextChar();
                                    //save = TRUE;
                                    state = StateType.DONE;
                                    currentToken.Lex = LexType.ERROR1;
                                    global.operateError = true;
                                }
                            }
                            else
                            {
                                ungetNextChar();
                                //save = TRUE;
                                state = StateType.DONE;
                                currentToken.Lex = LexType.ERROR1;
                                global.operateError = true;
                            }
                            break;
                        case StateType.INID:

                            /* 当前字符c不是字母,则在输入行缓冲区源中回退一个字符		 			  *
                             * 字符存储标志设置为FALSE,当前DFA状态state设置为DONE,标识符单词识别完成 *
                             * 当前识别单词返回值currentToken设置为标识符单词ID                      */
                            if (!isalnum(c))
                            {
                                ungetNextChar();
                                save = false;
                                state = StateType.DONE;
                                currentToken.Lex = LexType.ID;
                            }
                            break;
                        case StateType.DONE: break;

                        /* 当前DFA状态state为其它状态,此种情况不应发生 */
                        default:

                            /* 将词法扫描器产生错误的状态state写入列表文件listing	*
                             * 当前DFA状态state设置为完成状态DONE					*
                             * 当前识别单词返回值currentToken设置为错误单词ERROR1	*/
                            mainWindow.outPutLine("Scanner Bug: state= " + state + "\n");
                            state = StateType.DONE;
                            currentToken.Lex = LexType.ERROR1;
                            global.operateError = true;
                            break;
                    }//第一层switch
                    if (save)
                        tokenString += (char)c;
                    if (state == StateType.DONE)
                    {
                        if (currentToken.Lex == LexType.ID)
                        {
                            currentToken.Lex = reservedLookup(tokenString);
                        }
                    }
                }//while
                currentToken.lineshow = global.operatelineno;
                currentToken.Sem = tokenString;
                list.Add(currentToken);
                global.operateTokentnum = global.operateTokentnum + 1;
                mainWindow.outPutLine("tokenString: " + tokenString);
                tokenString = "";
            } while ((currentToken.Lex) != LexType.ENDFILE1);
            mainWindow.outPutLine("end of get token list");
            source.Close();

        }


        public void printTokenlist()
        {
            mainWindow.outPutLine("输出TokenList（即是list）中信息...");
            int num;
            TokenType currentToken;
            if (global.operateError)
                mainWindow.outPutLine("----词法分析有错，以ERROR打头的为错误的Token：\n");
            else mainWindow.outPutLine("----词法分析无错：Token序列如下：\n");
            int count = list.Count;
            if (count <= 0)
            {
                mainWindow.outPutLine("Token序列为空...");
                return;
            }
            for (num = 0; num < global.operateTokentnum; num++)
            {
                currentToken = list[num];
                mainWindow.outPut("行号: " + currentToken.lineshow);
                printToken(currentToken);
            }
        }

        protected int getNextChar()
        {
            if (!(linepos < length))
            {
                global.operatelineno = global.operatelineno + 1;
                if ((lineBuf = source.ReadLine()) != null)
                {
                    lineBuf = "   " + lineBuf;
                    mainWindow.outPutLine("buffer: " + lineBuf);
                    length = lineBuf.Length;
                    mainWindow.outPutLine("buffer length: " + length);
                    linepos = 0;

                    return lineBuf[linepos++];
                }
                else
                {
                    /* 未能成功读入新的代码行,fget函数返回值为NULL ,已经到源代码文件末尾,设置EOF_flag标志为TRUE */
                    EOF_flag = true;
                    return -1;
                }
            }/* 行输入缓冲区lineBuf中字符还未读完,直接取其中下一字符,函数返回所取字符 */
            else return lineBuf[linepos++];
        }


        protected void ungetNextChar()
        {//将字符重新放回缓冲区中
            if (!EOF_flag)
                linepos--;
        }

        protected void ChainToFile(List<TokenType> li) { }//token序列保存到文件

        protected void printToken(TokenType token)
        {
            switch (token.Lex)
            {
                case LexType.PROGRAM:
                case LexType.PROCEDURE:
                case LexType.TYPE:
                case LexType.VAR:
                case LexType.IF:
                case LexType.THEN:
                case LexType.ELSE:
                case LexType.FI:
                case LexType.WHILE:
                case LexType.DO:
                case LexType.ENDWH:
                case LexType.BEGIN:
                case LexType.END:
                case LexType.READ:
                case LexType.WRITE:
                case LexType.ARRAY:
                case LexType.OF:
                case LexType.RECORD:
                case LexType.RETURN:
                case LexType.INTEGER:
                case LexType.CHAR1:
                    mainWindow.outPutLine("reserved word: " + token.Sem);
                    break;
                case LexType.ASSIGN: mainWindow.outPutLine(" :="); break;

                /* 单词token为特殊符号：LT (小于)，将"<"写入文件listing */
                case LexType.LT: mainWindow.outPutLine(" <"); break;

                /* 单词token为特殊符号：EQ (等于)，将"="写入文件listing */
                case LexType.EQ: mainWindow.outPutLine(" ="); break;

                /* 单词token为特殊符号：PLUS (加号)，将"+"写入文件listing */
                case LexType.PLUS: mainWindow.outPutLine(" +"); break;

                /* 单词token为特殊符号；MINUS (减号)，将"-"写入文件listing */
                case LexType.MINUS: mainWindow.outPutLine(" -"); break;

                /* 单词token为特殊符号：TIMES (乘号)，将"*"写入文件listing */
                case LexType.TIMES: mainWindow.outPutLine(" *"); break;

                /* 单词token为特殊符号：OVER (除号)，将"/"写入文件listing */
                case LexType.OVER: mainWindow.outPutLine(" /"); break;

                /* 单词token为特殊符号：LPAREN (左括号)，将"("写入文件listing */
                case LexType.LPAREN: mainWindow.outPutLine(" ("); break;

                /* 单词token为特殊符号：RPAREN (右括号)，将")"写入文件listing */
                case LexType.RPAREN: mainWindow.outPutLine(" )"); break;

                /* 单词token为特殊符号：LMIDPAREN (左中括号)，将"["写入文件listing */
                case LexType.LMIDPAREN: mainWindow.outPutLine(" ["); break;

                /* 单词token为特殊符号：RMIDPAREN (右中括号)，将"]"写入文件listing */
                case LexType.RMIDPAREN: mainWindow.outPutLine(" ]"); break;

                /* 单词token为特殊符号：SEMI (分号)，将";"写入文件listing */
                case LexType.SEMI: mainWindow.outPutLine(" ;"); break;

                /* 单词token为特殊符号：COLON (冒号)，将":"写入文件listing */
                case LexType.COLON: mainWindow.outPutLine(" :"); break;

                /* 单词token为特殊符号：COMMA (逗号)，将","写入文件listing */
                case LexType.COMMA: mainWindow.outPutLine(" ,"); break;

                /* 单词token为特殊符号：DOT (程序结束符)，将"."写入文件listing */
                case LexType.DOT: mainWindow.outPutLine(" ."); break;

                /* 单词token为特殊符号：UNDERANGE (下标界限)，将".."写入文件listing */
                case LexType.UNDERANGE: mainWindow.outPutLine(" .."); break;

                /* 单词token为簿记单词符号：ENDFILE (文件结尾)，将EOF写入文件listing */
                case LexType.ENDFILE1: mainWindow.outPutLine(" EOF"); break;

                /* 单词token为多字符单词符号：INTC (数字)，将数值写入文件listing */
                case LexType.INTC:
                    mainWindow.outPutLine("INTC, val = " + token.Sem);
                    break;

                /* 单词token为多字符单词符号：CHARC (字符)，将字符写入文件listing */
                case LexType.CHARC:
                    mainWindow.outPutLine("CHARC, letter = " + token.Sem);
                    break;

                /* 单词token为多字符单词符号：ID (标识符)，将标识符名写入文件listing */
                case LexType.ID:
                    mainWindow.outPutLine("ID, name = " + token.Sem);
                    break;

                /* 单词token为簿记单词符号：ERROR (错误)，将错误信息写入文件listing */
                case LexType.ERROR1:
                    mainWindow.outPutLine("ERROR: " + token.Sem);
                    break;

                /* 单词token为其他未知单词，未知信息写入文件listing,此种情况不应发生 */
                default:
                    mainWindow.outPutLine("Unknown token: " + token.Lex);
                    break;
            }
        }

        protected LexType reservedLookup(String str)
        {
            int i;
            for (i = 0; i < reservedWords.Length; i++)
                if (reservedWords[i].str.Equals(str))
                    /* 字符串s与保留字表中某一表项匹配,函数返回对应保留字单词 */
                    return (reservedWords[i].tok);

            /* 字符串s未在保留字表中找到,函数返回标识符单词ID */
            return LexType.ID;
        }

        protected bool isdigit(int i)
        {
            if (i >= 48 && i <= 57) return true;
            else return false;
        }

        protected bool isalpha(int c)
        {
            if ((c >= 65 && c <= 90) || (c >= 97 && c <= 122)) return true;
            else return false;
        }

        protected bool isalnum(int c)
        {
            bool flag;
            flag = isdigit(c);
            if (flag == false)
                flag = isalpha(c);
            return flag;
        }
    }   //  class WordParser

}   //  namespace CourseDesign_01_LexicalAnalysis
